#pragma once

#include <memory>
#include <cmath>
#include "rtda/frame.h"
#include "instructions/base/bytecode_reader.h"
#include "instructions/base/instructions.h"

namespace jvm {
namespace instructions {

// Add instructions
class DADD : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class FADD : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class IADD : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LADD : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Subtract instructions
class DSUB : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class FSUB : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class ISUB : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LSUB : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Multiply instructions
class DMUL : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class FMUL : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class IMUL : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LMUL : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Divide instructions
class DDIV : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class FDIV : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class IDIV : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LDIV : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Remainder instructions
class DREM : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class FREM : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class IREM : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LREM : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Negate instructions
class DNEG : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class FNEG : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class INEG : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LNEG : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Bitwise AND instructions
class IAND : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LAND : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Bitwise OR instructions
class IOR : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LOR : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Bitwise XOR instructions
class IXOR : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LXOR : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Shift instructions
class ISHL : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class ISHR : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class IUSHR : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LSHL : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LSHR : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};
class LUSHR : public NoOperandsInstruction {
public: void execute(std::shared_ptr<rtda::Frame> frame) override;
};

// Increment local variable by constant
class IINC : public Instruction {
private:
    uint32_t _index;
    int32_t _const;
    
public:
    void fetchOperands(std::shared_ptr<BytecodeReader> reader) override;
    void execute(std::shared_ptr<rtda::Frame> frame) override;

    void setIndex(uint32_t index);
    void setConst(int32_t constant);
    uint32_t getIndex() const;
    int32_t getConst() const;
};

} // namespace instructions
} // namespace jvm